-- octopus.lua
-- implements an octopus, duh.

octopus = {
    kind = "octopus",

	-- movement
	pos = vec2:new(),
	vel = vec2:new(),
	target = vec2:new(),
	speed = 0, -- speed the octopus is going
	ang = 0, -- angle octopus is pointing
	set_ang = 0, -- the angle the octopus wants to point
	ang_rate = 6, -- rate at which angle slews to desired angle, in radians per second
	decel = 4, -- rate at which octopus slows while moving through water

	rocket = false, 
	
	-- state stuff
	mode = "speed", -- current mode, either "speed" or "stealth"
	hit_points = 8, -- octopus life
	score = 0, -- MASSIVE DAMAGE
	charge_lunge = -1, -- seconds charging lunge
	big_lunge = 0, -- countdown after a huge lunge
	
	death_spin = -1, -- I'm dying!

	-- camo stuff
	invisible = false, -- false if player is visible to enemies, true if hidden
	stored_camo = {}, -- stored camo type
	camo_sample_timer = -1, -- used when sampling a new camo
	camo_sample_tile = nil, -- the tile octopus is picking up
	
	-- textures
	head_tex = {},

	-- drawing stuff
	fade = 1, -- fades from normal texture (0 for from texture, 1 for to texture)
	from_tex = 0, to_tex = 0,
	from_tint_r = 255, from_tint_g = 255, from_tint_b = 255, -- tint
	to_tint_r = 255, to_tint_g = 255, to_tint_b = 255, -- tint

	-- physics stuff
    tentacles = {},
    tentacle_joints = {},
    body   = nil,
    shapes = nil,
}

function octopus:setupGraphics()
	self.head_tex[0] = love.graphics.newImage("images/Octopus/Octotoon_02.png")
	self.head_tex[tile_id["sand"]] = love.graphics.newImage("images/Octopus/Octotoon_03.png")
	self.head_tex[tile_id["gravel"]] = love.graphics.newImage("images/Octopus/Octotoon_04.png")
	self.head_tex[tile_id["coral_g"]] = love.graphics.newImage("images/Octopus/Octotoon_05.png")
	self.head_tex[tile_id["coral_p"]] = love.graphics.newImage("images/Octopus/Octotoon_06.png")
	self.head_tex[tile_id["coral_r"]] = love.graphics.newImage("images/Octopus/Octotoon_07.png")
	self.head_tex[tile_id["rocks"]] = love.graphics.newImage("images/Octopus/Octotoon_08.png")
	
	self.stored_camo = { tile_id["sand"] }
end

tentacleIndexToPosMap = {
   { -45, 25 },
   { -45, -30 },
   { -65, 100 },
   { -55, -105 },
   { -35, -85 },
   { -40, 95 },
   { -25, -55 },
   { -15, 65 },
}

tentacleIndexToOffsetMap =
{
   math.pi / 2,
   4 * math.pi / 6,
   3 * math.pi / 4,
   5 * math.pi / 6,
   7 * math.pi / 6,
   5 * math.pi / 4,
   8 * math.pi / 6,
   3 * math.pi / 2
}

function octopus:init( world, x, y )
   local shape = nil
   self.shapes = {}
   self.pos.x = x
   self.pos.y = y

   self.body = love.physics.newBody( world, x, y )

   shape = love.physics.newCircleShape( self.body, 0, 0, 80 )
   shape:setDensity( 4 )
   shape:setData( self )
   table.insert( self.shapes, shape )

   shape = love.physics.newCircleShape( self.body, 140, 0, 70 )
   shape:setDensity( 4 )
   shape:setData( self )
   table.insert( self.shapes, shape )

   shape = love.physics.newCircleShape( self.body, -50, 105, 35 )
   shape:setDensity( 4 )
   shape:setData( self )
   table.insert( self.shapes, shape )

   shape = love.physics.newCircleShape( self.body, -50, -105, 35 )
   shape:setDensity( 4 )
   shape:setData( self )
   table.insert( self.shapes, shape )

   self.body:setLinearDamping( 1 )
   self.body:setMassFromShapes()
   self.body:setAllowSleeping( false )

   for i,tentacle_pos in ipairs( tentacleIndexToPosMap ) do
      local tentacle_x = x + tentacle_pos[1]
      local tentacle_y = y + tentacle_pos[2]

      tentacle = Tentacle:new()
      tentacle:init( world, self, tentacle_x, tentacle_y )

      tentacle_joint = love.physics.newRevoluteJoint( self.body,
                                                      tentacle.bodies.high,
                                                      tentacle_x, tentacle_y )
      tentacle_joint:setLimitsEnabled( true )
      tentacle_joint:setLimits( 0, math.pi / 2 )
      tentacle_joint:setMotorEnabled( true )
      tentacle_joint:setMotorSpeed( 0 )
      tentacle_joint:setMaxMotorTorque( 1.0 / 0.0 )

      table.insert( self.tentacles, tentacle )
      table.insert( self.tentacle_joints, tentacle_joint )
   end

end

function octopus:addScore(val)
	self.score = self.score + val
	-- pop off score particles
	particle:newPoints(self.pos, val)
end

function octopus:getPos()
   return vec2:init( self.body:getX(), self.body:getY() )
end

function octopus:setTarget(mx, my)
	self.target = vec2:init(mx, my)
	local d = self.target - self:getPos()
	self.set_ang = d:ang()
	--self.speed = d:len()
end

function octopus:setAngle(angle)
   self.ang = ang_norm(angle)
   self.body:setAngle( angle )
end

function octopus:swapCamo(kind, tr, tg, tb)
	tr = tr or 255
	tg = tg or 255
	tb = tb or 255
	
	if self.head_tex[kind] and kind ~= self.to_tex then
		self.from_tex = self.to_tex
		self.from_tint_r, self.from_tint_g, self.from_tint_b = self.to_tint_r, self.to_tint_g, self.to_tint_b

		self.to_tex = kind
		self.to_tint_r, self.to_tint_g, self.to_tint_b = tr, tg, tb

		self.fade = 0

       for tentacle_index, tentacle in ipairs( self.tentacles ) do
          tentacle:setDrawParams( self.from_tex, self.from_tint_r, self.from_tint_g, self.from_tint_b,
                                  self.to_tex, self.to_tint_r, self.to_tint_g, self.to_tint_b )
          tentacle:setFade( self.fade )
       end
	end
end

function octopus:damage()
	self.hit_points = self.hit_points - 1
	-- pop off a limb here
   tentacle_joint = table.remove( self.tentacle_joints )

   if tentacle_joint == nil then
      -- GAME OVER
      if self.death_spin < 0 then
	      self.death_spin = 2
      end
      return
   end

   tentacle_joint:destroy()

   tentacle = table.remove( self.tentacles )
   table.insert( obj, tentacle )
end

function octopus:toggle()
	if self.camo_sample_timer >= 0 then
		return
	end

	if self.mode == "speed" then
		self.mode = "stealth"
		getCamera().scale_target = 0.7
		self:swapCamo(unpack(self.stored_camo))
	else
		self.mode = "speed"
		self:swapCamo(0)
		getCamera().scale_target = 0.5
	end
end

function octopus:lunge()
	self.speed = self.charge_lunge * 6000
	
	if self.speed < 3000 then
		self.speed = 3000
	end
	
	if self.speed > 6000 then
		-- SOUND 
		love.audio.play(audio.jets[randint(1, 5)])
	end
	
	if self.speed > 12000 then
		self.speed = 12000
	end
	
	if self.mode == "stealth" then
		self:toggle()
		self.big_lunge = .2
	end
	
	self.charge_lunge = -1
end

function octopus:update(dt)
   local dtarg = self.target - self:getPos()
	local dlen = dtarg:len()
	
	if self.hit_points <= 0 then
		self.speed = 0
		self.vel = vec2:new()
		
		self.ang = ang_norm(self.ang + dt * rad(540))
		
		self.death_spin = self.death_spin - dt
		if self.death_spin < 0 then
			game_over = true
		end
		
		return
	end
	
	if self.charge_lunge >= 0 then
		self.charge_lunge = self.charge_lunge + dt
	end
	
	self.big_lunge = self.big_lunge - dt
	self.invisible = false

	local fast_cruise = 600
	local slow_cruise = 200
	
	local tile = tile_world:lookup(self:getPos())
	
	if self.mode == "speed" then
		ang_rate = 6
		-- handle speed
--		if self.speed <= fast_cruise then
--			if dlen > 200 then
--				if dlen < fast_cruise then
--					self.speed = dlen
--				else
--					self.speed = fast_cruise
--				end
--			else
--				self.speed = 0
--			end
--		end
		
		self.invisible = false
		
	else -- self.mode == "stealth"
		ang_rate = 99
		
		-- handle speed
		if dlen > 40 then
			self.speed = dlen
		else
			self.speed = 0
		end
		
		if self.speed > slow_cruise then
			self.speed = slow_cruise
		end
	
		-- handle camo
		if tile then
			-- figure out if we're visible or not
			if tile.kind == self.to_tex then
				self.invisible = true
			end
			for i, v in ipairs(tile.blends) do
				if v.kind == self.to_tex then
					self.invisible = true
					break
				end
			end
		else
			self:swapCamo(0)
		end
	end
	
--	bug = tostring(self.invisible)
	
	-- camo recording
	if self.camo_sample_timer >= 0 then
		if self.camo_sample_timer > 2 then
			if tile then
				self.stored_camo = tile:grabTex()
				self:swapCamo(unpack(self.stored_camo))
			end
		else
			if self.camo_sample_timer > 0.3 then
				self.speed = 10
			end
			self.camo_sample_timer = self.camo_sample_timer + dt
		end
	end
	
	-- spit bubbles
	if self.big_lunge > -.5 then
		particle:newBubble(self.pos)
	end

	-- handle slewing towards desired angle
	if dtarg:len() > 60 and self.big_lunge < 1 then
		local d = ang_diff(self.set_ang, self.ang)
		local da = dt * self.ang_rate
		if math.abs(d) < da then
             self:setAngle( self.ang )
		elseif d < 0 then
             self:setAngle( self.ang - da )
		else --if d > 0 then
             self:setAngle( self.ang + da )
		end
	end
	
	-- dead stop if cloaked and charging for a lunge
	if self.mode == "stealth" and self.charge_lunge >= 0 then
		self.speed = 0
	end
	
	if self.rocket then
		self.speed = 8000
	end
		
	-- handle speed computation
	self.speed = self.speed - (self.speed * dt * self.decel)
	self.vel = self.speed * vec2:dir(self.ang)

	-- correct for illegal tiles
	if not self.rocket and tile and tile.kind == tile_id["border"] then
		local pdir = {}
		
		self:pushList(pdir)
		
		for i, v in ipairs(pdir) do
			local vdot = dot(self.vel, v)
			
			if vdot < 0 then
				self.vel = self.vel - vdot * v
			end
		end
	else
	end
	
	-- physics update
--    self.body:setLinearVelocity( self.vel.x, self.vel.y )
--    self.body:setAngle( self.ang )
    self.pos = self.pos + dt * self.vel

    self.body:setX( self.pos.x )
    self.body:setY( self.pos.y )
	
    -- handle camo fading
    self.fade = self.fade + 3 * dt * (1 - self.fade)
    for tentacle_index, tentacle in ipairs( self.tentacles ) do
       tentacle:setFade( self.fade )
    end
end

function octopus:pushList(sum)
	local r = 100
	
   table.insert(sum, tile_world:pushDir(self:getPos() + vec2:init(r, 0)))
   table.insert(sum, tile_world:pushDir(self:getPos() + vec2:init(-r, 0)))
   table.insert(sum, tile_world:pushDir(self:getPos() + vec2:init(0, r)))
   table.insert(sum, tile_world:pushDir(self:getPos() + vec2:init(0, -r)))
end

function octopus:drawHead(alpha)
	local s = 0.8 -- overall scale factor

	local offx = 0 -- small correction applied to visually center octopus
	local offy = -70

	local tx = self.head_tex[self.from_tex]

	love.graphics.setColor(self.from_tint_r, self.from_tint_g, self.from_tint_b, 255)
	love.graphics.draw(tx, self:getPos().x, self:getPos().y, self.ang + rad(90), s, s, tx:getWidth()/2 + offx, tx:getHeight()/2 + offy)

	tx = self.head_tex[self.to_tex]

	love.graphics.setColor(self.to_tint_r, self.to_tint_g, self.to_tint_b, 255 * self.fade)
	love.graphics.draw(tx, self:getPos().x, self:getPos().y, self.ang + rad(90), s, s, tx:getWidth()/2 + offx, tx:getHeight()/2 + offy)
end

function octopus:draw()
    local x     = self.body:getX()
    local y     = self.body:getY()
    local angle = self.body:getAngle()
    local pos   = vec2:init( x, y )

	local e = vec2:new(self:getPos() +  30 * vec2:dir(self.ang))
	love.graphics.line(self:getPos().x, self:getPos().y, e.x, e.y)
	
--	e = tile_world:pushDir(self.pos)
--	e = self.pos + 300 * e 
--	love.graphics.line(self.pos.x, self.pos.y, e.x, e.y)

--	this block blows up with mirrored sprite rendering!  Seems to be a bug in love...
--    for tentacle_index,tentacle in pairs(self.tentacles) do
--       for shape_index,shape in pairs( tentacle.shapes ) do
--          love.graphics.polygon( "line", shape:getPoints() )
--       end
--    end


    for tentacle_index,tentacle in pairs(self.tentacles) do
       tentacle:draw()
       for shape_index,shape in pairs( tentacle.shapes ) do
--          drawBoundingBox( shape )
       end
    end

	love.graphics.setColor(255, 255, 255)
	self:drawHead()

    for shape_index, shape in ipairs( self.shapes ) do
--       drawBoundingCircle( shape )
    end

    love.graphics.setColor( 255, 0, 0 )

    for tentacle_joint_index,tentacle_joint in pairs(self.tentacle_joints) do
       ax1, ay1, ax2, ay2 = tentacle_joint:getAnchors()
--       love.graphics.circle( "line", ax1, ay1, tentacle_width / 2, 8 )
    end

    for tentacle_index,tentacle in pairs(self.tentacles) do
       for joint_index,joint in pairs( tentacle.joints ) do
          ax1, ay1, ax2, ay2 = joint:getAnchors()
--          love.graphics.circle( "line", ax1, ay1, tentacle_width / 2, 8 )
       end
    end
end
